package org.zero.onlinesubject.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.zero.onlinesubject.common.constant.RedisConstant;
import org.zero.onlinesubject.common.vo.CourseVo;
import org.zero.onlinesubject.common.vo.PageResult;
import org.zero.onlinesubject.entity.*;
import org.zero.onlinesubject.mapper.*;
import org.springframework.stereotype.Service;
import org.zero.onlinesubject.service.SystemFileService;
import org.zero.onlinesubject.utils.IDWorker;
import org.zero.onlinesubject.utils.UserContextHolder;
import org.zero.onlinesubject.service.CourseService;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 *
 */
@Service
@Slf4j
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService{

    @Value("${file.external_path}")
    private String externalPath;

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private CourseTypeMapper courseTypeMapper;

    @Resource
    private TagTypeMapper tagTypeMapper;

    @Resource
    private ChapterMapper chapterMapper;

    @Resource
    private ChapterVideoMapper chapterVideoMapper;

    @Resource
    private SystemFileService systemFileService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ThreadService threadService;

    @Resource
    private Executor taskExecutor;

    @Override
    public List<CourseType> getCourseTypeList() {
        return courseTypeMapper.selectList(null);
    }

    //  添加课程
    @Override
    public Boolean addCourse(Course course) {
        // 判断用户身份是否是老师只有老师才有权限添加课程
        User user = UserContextHolder.get();
        if(user.getRoleid() !=3){
            throw new RuntimeException("当前角色没有发布课程的权限");
        }
        course.setTeacherid(user.getUserid());

        UpdateWrapper<CourseType> wrapper = new UpdateWrapper<>();
        courseTypeMapper.courseCountAdd(course.getClstype());

        return save(course);
    }

    // 获取课程列表
    @Override
    public PageResult<Course> getCourseList(String userId, Integer start, Integer end) throws ExecutionException, InterruptedException {
        List<Course> courseList = null;
        CompletableFuture<List<Course>> f1 = CompletableFuture.supplyAsync(() -> {
            return userId != null ? courseMapper.getCourseListById(userId, start, end): courseMapper.getCourseList(start, end);
        }, taskExecutor);

        CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
            return userId!=null ? courseMapper.getUserCourseCount(userId) : courseMapper.getCourseListCount();
        }, taskExecutor);
        courseList = f1.get();
        courseList.forEach(c->{
            c.setLogoimg(externalPath + c.getLogoimg());
        });
        Integer total = f2.get();

        return new PageResult<Course>(courseList, start, total);
    }

    @Override
    public Boolean updateCourse(Course course) {
        // 校验当前用户是否是本课程的发布者,只有本课程的发布者或者是管理员才权利修改课程信息
        User user = UserContextHolder.get();
        if(user.getRoleid()!=1 || user.getRoleid()!=3 || !Objects.equals(course.getTeacherid(), user.getUserid())){
            throw new RuntimeException("当前用户没有修改权限");
        }
        return courseMapper.updateById(course)!=0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean delCourse(String courseId) throws ExecutionException, InterruptedException, TimeoutException {
        Course course = getById(courseId);
        if(course==null){
            throw new RuntimeException("要删除的课程信息不存在");
        }
        // 校验当前用户是否是本课程的发布者,只有本课程的发布者或者是管理员才权利修改课程信息
        User user = UserContextHolder.get();
        if(!Objects.equals(course.getTeacherid(),user.getUserid()) && user.getRoleid()!=1){
            throw new RuntimeException("当前用户没有删除权限");
        }

        List<Chapter> chapterList = chapterMapper.selectByClsID(courseId);
        List<String> cpIds = chapterList.stream().map(Chapter::getChapterid).collect(Collectors.toList());

        List<ChapterVideo> videoList = null;
        List<String> bizIds = null;
        Boolean flag1 = true;
        if(!cpIds.isEmpty()){
            QueryWrapper<ChapterVideo> wrapper = new QueryWrapper<>();
            wrapper.select("VideoID");
            wrapper.in("ChapterID",cpIds);
            videoList = chapterVideoMapper.selectList(wrapper);
            bizIds = videoList.stream().map(ChapterVideo::getVideoid).collect(Collectors.toList());
            flag1  = systemFileService.deleteFileByBizID(bizIds);
            String resStr1 = flag1 ? "删除文件成功":"删除文件失败";
            log.info(resStr1);
        }

        boolean flag2 = removeById(courseId);
        String resStr2 = flag2 ? "删除课程成功":"删除课程失败";
        log.info(resStr2);
        QueryWrapper<Chapter> delWrapper = new QueryWrapper<>();
        delWrapper.eq(!StringUtils.isNullOrEmpty(courseId),"CourseID",courseId);
        chapterMapper.delete(delWrapper);
        courseMapper.deleteUserCourse(courseId);
        threadService.deleteCourseCache(stringRedisTemplate,courseId,user.getUserid());

        if(!flag2){
            throw new RuntimeException("删除课程信息失败,事务回滚");
        }
        return true;
    }



    @Override
    public int getUserCourseCount(String userId) {
        return courseMapper.getUserCourseCount(userId);
    }

    @Override
    public int getCourseListCount(){
        return courseMapper.getCourseListCount();
    }

    @Override
    public Boolean joinCourse(String userId,String courseId) {
        Integer rows = courseMapper.isExists(userId, courseId);
        if(rows > 0) return false;
        return courseMapper.userJoinCourse(userId, courseId) != 0;
    }

    @Override
    public List<CourseVo> getHot() {
        List<CourseVo> voList = courseMapper.getHot();
        voList.forEach(v->{
            v.setLogoimg(externalPath + v.getLogoimg());
        });

        return voList;
    }

    @Override
    public Boolean addChapterVideo(ChapterVideo chapterVideo,String courseId) {
        Integer order = getLastOrderByChapterId(chapterVideo.getChapterid());
        chapterVideo.setOrder(order);
        int i = chapterVideoMapper.insertOne(chapterVideo);
        System.out.println("i=============>"+i);
        String body = stringRedisTemplate.opsForValue().get(RedisConstant.CHAPTER_VIDEO_PREFIX + courseId);
        if(!StringUtils.isNullOrEmpty(body)){
            if(i == 0) return false;
            List<ChapterVideo> list = JSON.parseObject(body, new TypeReference<List<ChapterVideo>>() {});
            list.add(chapterVideo);
            stringRedisTemplate.opsForValue().set(RedisConstant.CHAPTER_VIDEO_PREFIX+courseId,JSON.toJSONString(list),2, TimeUnit.HOURS);
        }
        return true;
    }

    private Integer getLastOrderByChapterId(String chapterId) {
        QueryWrapper<ChapterVideo> wrapper = new QueryWrapper<>();
        wrapper.select("`Order`").eq("ChapterID",chapterId).orderByDesc("`Order`").last(" limit 1");
        ChapterVideo video = chapterVideoMapper.selectOne(wrapper);
        return video == null ? 1 : video.getOrder();
    }

    @Override
    public PageResult<CourseVo> getCourseByCond(String tagCode, String typeCode, Integer current, Integer size) throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<List<CourseVo>> f1 = CompletableFuture.supplyAsync(() -> {
            return courseMapper.getCourseByCond(tagCode, typeCode, current, size);
        }, taskExecutor);

        CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
            return courseMapper.getCourseCountByCond(tagCode, typeCode, current, size);
        }, taskExecutor);

        List<CourseVo> voList = f1.get(3, TimeUnit.SECONDS);
        voList.forEach(v->{
            v.setLogoimg(externalPath+v.getLogoimg());
        });

        PageResult<CourseVo> result = new PageResult<>();
        result.setCurrent(current);
        result.setTotal(f2.get(3, TimeUnit.SECONDS));
        result.setData(voList);

        return result;
    }

    @Override
    public PageResult<CourseVo> getCourseBySearch(String courseName,Integer current,Integer size) throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<List<CourseVo>> f1 = CompletableFuture.supplyAsync(() -> {
            List<CourseVo> voList = courseMapper.getCourseBySearch(courseName,current,size);
            voList.forEach(v -> {
                v.setLogoimg(externalPath + v.getLogoimg());
            });
            return voList;
        },taskExecutor);

        CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
            return courseMapper.getCourseSearchCount(courseName);
        }, taskExecutor);

        List<CourseVo> voList = f1.get(5,TimeUnit.SECONDS);

        int total = f2.get(3,TimeUnit.SECONDS);

        PageResult<CourseVo> result = new PageResult<>();
        result.setCurrent(current);
        result.setTotal(total);
        result.setData(voList);
        return result;
    }

    @Override
    public List<CourseVo> getOwnCourse(String userid) {
        List<CourseVo> list = courseMapper.getOwnCourse(userid);
        list.forEach(item->{
            item.setLogoimg(externalPath + item.getLogoimg());
        });
        return list;
    }

    @Override
    public List<TagType> getTagList() {
        return tagTypeMapper.selectList(null);
    }

    @Override
    public CourseVo getCourseVo(String courseId) {
        CourseVo vo = null;
        String json = stringRedisTemplate.opsForValue().get(RedisConstant.COURSE_INFO_PREFIX+courseId);
        if(!StringUtils.isNullOrEmpty(json)){
            vo = JSON.parseObject(json,CourseVo.class);
            if(UserContextHolder.get()!=null){
                String userid = UserContextHolder.get().getUserid();
                Integer rows = courseMapper.isExists(userid, courseId);
                vo.setStudied(rows!=0);
            }
            return vo;
        }
        vo = courseMapper.getCourseVo(courseId);
        if(UserContextHolder.get()!=null){
            String userid = UserContextHolder.get().getUserid();
            Integer rows = courseMapper.isExists(userid, courseId);
            vo.setStudied(rows!=0);
        }

        vo.setLogoimg(externalPath + vo.getLogoimg());
        stringRedisTemplate.opsForValue().set(RedisConstant.COURSE_INFO_PREFIX+courseId,JSON.toJSONString(vo),30,TimeUnit.MINUTES);

        return vo;
    }

    @Override
    public List<CourseVo> getMyLearn() {
        User user = UserContextHolder.get();
        List<CourseVo> courseVos = courseMapper.getMyLearn(user.getUserid());
        courseVos.forEach(vo->{
            vo.setLogoimg(externalPath + vo.getLogoimg());
        });
        return courseVos;
    }

    @Override
    public List<Course> getTop5() {
        String json = stringRedisTemplate.opsForValue().get(RedisConstant.COURSE_TOP5);
        List<Course> courseList = null;
        if(!StringUtils.isNullOrEmpty(json)){
            courseList = JSON.parseObject(json, new TypeReference<List<Course>>() {});
        }

        courseList = courseMapper.getTop5();
        courseList.forEach(course->{
            course.setLogoimg(externalPath+course.getLogoimg());
        });
        stringRedisTemplate.opsForValue().set(RedisConstant.COURSE_TOP5, JSON.toJSONString(courseList),2,TimeUnit.HOURS);
        return courseList;
    }

    @Override
    public List<Course> getOwnCourseNameList(String userid) {
        String json = stringRedisTemplate.opsForValue().get(RedisConstant.USER_COURSE_NAME+userid);
        List<Course> list = null;
        if(!StringUtils.isNullOrEmpty(json)){
            list = JSON.parseObject(json,new TypeReference<List<Course>>(){});
            return list;
        }
        list = courseMapper.selectOwnCourseName(userid);
        stringRedisTemplate.opsForValue().set(RedisConstant.USER_COURSE_NAME+userid,JSON.toJSONString(list),30,TimeUnit.MINUTES);

        return list;
    }
}
